前面提到了許多篇重構的方式,都是偏向pattern或較大面向的設計重構,在面對比較大的系統包袱時,或許大家比較沒法子運用的得心應手,所以接下來會穿插一些誰都可以進行重構的技巧,希望讓大家對改善系統能更有衝動。
重構通常針對的就是兩個東西:判斷式與迴圈。這一篇文章會提到,怎麼樣重構我們的判斷式,使其更容易閱讀,更具備未來修改的彈性。
[如何提升系統品質]系列文章連結
範例說明
很無聊的在腦袋中,哼著『戲鳳』這首歌,稍微調整了一下,就變成了我們重構的目標了。程式碼如下:
/// <summary>
/// idea from :戲鳳, http://www.youtube.com/watch?v=JRs_bqW39oA
/// </summary>
public class DrinkingStore
{
public Person Boss { get; set; }
public bool IsBusinessDay(DateTime date, Person customer)
{
//一三五不賣酒
if (date.DayOfWeek == DayOfWeek.Monday || date.DayOfWeek == DayOfWeek.Wednesday || date.DayOfWeek == DayOfWeek.Friday ||
//初一十五不賣酒
date.Day == 1 || date.Day == 15 ||
//老闆不爽不賣酒, 老闆哥哥不在家不賣酒
this.Boss.IsAngry || this.Boss.Brother == null ||
//客人沒錢不賣酒//客人太醜不賣酒//客人太胖不賣酒
customer.IsPoor || customer.IsUgly || customer.IsFat)
{
return false;
}
else
{
return true;
}
}
}
可以看到程式中,一個if判斷式,裡面要判斷的條件落落長,而且這種條件判斷,很常修改或新增其他新的需求。第一版的程式,可能只有寫『哥哥不在家,今天不賣酒』,隨著情況越來越多,程式經手越來越多人,最後程式就長這樣。
當我們看到這樣的程式,你可以選擇:
反正現在的程式活的好好的,不要去改他。前人都這樣加上去,我們就跟著這樣加上去。
一塊一塊的抽出來,沒有動到架構,我有100%信心把這一段程式寫的更人性化且不會衍生問題。
看完這篇文章,希望大家都可以勇敢的選2!
重構步驟
步驟一:
首先,我們先抽象地瞭解這個function要提供什麼功能。
1.根據條件來決定,賣不賣酒
2.條件有分成幾種類型:
(1)根據『日期中的星期幾』來決定
(2)根據『日期中的日子』來決定
(3)根據『老闆的一堆毛』來決定
(4)根據『客人的一堆毛』來決定
步驟二:
根據我們剛剛分析的第二點,一項一項把我們的條件拆開。
調整完的程式碼:
/// <summary>
/// idea from :戲鳳, http://www.youtube.com/watch?v=JRs_bqW39oA
/// </summary>
public class DrinkingStore
{
public Person Boss { get; set; }
public bool IsBusinessDay(DateTime date, Person customer)
{
if (WithoutSellingByDayOfWeek(date) ||
WithoutSellingByDay(date) ||
WithoutSellingByBoss() ||
WithoutSellingByCustomer(customer))
{
return false;
}
else
{
return true;
}
}
/// <summary>
/// 一三五不賣酒
/// </summary>
/// <param name="date"></param>
/// <returns></returns>
private static bool WithoutSellingByDayOfWeek(DateTime date)
{
return date.DayOfWeek == DayOfWeek.Monday || date.DayOfWeek == DayOfWeek.Wednesday || date.DayOfWeek == DayOfWeek.Friday;
}
/// <summary>
/// 初一十五不賣酒
/// </summary>
/// <param name="date"></param>
/// <returns></returns>
private static bool WithoutSellingByDay(DateTime date)
{
return date.Day == 1 || date.Day == 15;
}
/// <summary>
/// 老闆不爽不賣酒, 老闆哥哥不在家不賣酒
/// </summary>
/// <returns></returns>
private bool WithoutSellingByBoss()
{
return this.Boss.IsAngry || this.Boss.Brother == null;
}
/// <summary>
/// 客人沒錢不賣酒
/// 客人太醜不賣酒
/// 客人太胖不賣酒
/// </summary>
/// <returns></returns>
private bool WithoutSellingByCustomer(Person customer)
{
return customer.IsPoor || customer.IsUgly || customer.IsFat;
}
}
步驟三:
因為這個例子的判斷式裡面,因素有點多,為了可讀性,我會再宣告幾個變數來接各個情況回傳的bool值。透過bool變數的命名,會讓判斷式看起來更容易瞭解意思。
步驟四:
最後,因為我們這個function也是要回傳bool,所以可以連最後一個if都直接拿掉。
這樣我們的function,跟重構前的程式比起來,有沒好讀很多? 不管那些條件是不是一堆or跟一堆and交錯,最後都可以將判斷式簡化成比較抽象地概念。
當抽象完成後,未來如果是還有其他條件加進來,我們可以看該條件是否可以歸在已經定好的分類,來決定要新增一個bool跟function,或直接新增在原有的function中。這樣一來,最高階的IsBusinessDay的function,就不容易因為條件細節而需要一直變動。當出現問題或需求異動時,我們也可以很快速的調整設計。
結論
雖然只是簡單的判斷式重構,但這一招真的是簡單好用,可以用很小的成本,馬上讓原本花撒撒的程式變乾淨。
另外要提醒的一點是,if的判斷式裡面,就不要再出現判斷某個bool變數、bool屬性或function回傳bool值是 == true或== false了,因為bool就代表了true/false,就可以直接用來套上判斷式的意義。
嗚嗚嗚...有時候就是懶啊..
哈,我的原則是事不過三,第一次可以懶,第二次可以只修,第三次就要避免問題或需求異動一直發生了。複製貼上也是,一式兩份還在容許範圍,三份就代表有問題了
坦白說沒達到效果
我以前也會這樣做
但每個老闆,夥伴,使用者概念不同
最後我覺得在程序式裡直接寫,人家一看就懂較易維護
有些人不喜歡程式跳來跳去的或寫一些幫助的方法
有些人不喜歡OO你要看Code要跳來要去還要一層一層的看
除非整個Team有共識
不然就用最白痴,最直覺的方式做
反正也沒幾行Code
這就是每個人的想法不同。
對我來說,如果上頭要的是具備彈性的架構,『好維護的架構』。
那其他成員不懂這種基本的趨勢跟技能,是他們失職。
沒必要一起爛,也沒必要為了自己一己之私去推什麼最新的技術或framework。
但我得說,也沒幾行code是你碰到的個案,就是有這種理由,所以才會需要這麼多重構的文章。
若您覺得重構跟OO都是沒必要的,我建議您這系列文章都不需要看囉,對您來說都是垃圾。您就繼續維持最白痴的作法,或許對您是最好的。
我沒有否定你,也沒有叫人否定你
如果你認為大家的想法都要跟你一樣,你就別PO了
重構的方式非常多種
OO是一種
有人重構
是讓Code更少
有人是為效能
有人為可讀性
有人為可維護性
....
這麼沒什麼在那裡P來P去,那毫無意義
一支程式
不同的規模,不同的功用,不同的主管,不同的客戶..就會有不同的評價
如果你是寫不同企業的程式,他們就會規定你們的寫法。有人規範要全部寫在一個檔,有的要切多層..
pantc328提到:
有人重構
是讓Code更少
有人是為效能
有人為可讀性
有人為可維護性
每家公司的要求是不一樣的...